package com.lh.spring.ioc;

import com.lh.spring.FrameUtil.FrameEnum;
import com.lh.spring.annotation.ioc.Bean;
import com.lh.spring.annotation.ioc.Component;
import com.lh.spring.annotation.Configuration;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description spring的入口类,实例化时会对Bean对象实例化同时将其放入ioc容器中
 * @Version 1.0.0
 * @Date 2024/1/14 14:55
 * @Created by LiuHua
 * @Path com.lh.spring.ioc
 */
@Component
public class ApplicationClass extends BeanFactory{
    //bean的包扫描路径
    private String beanPackage;

    private Class aClass;
    protected static FrameEnum enumToCase=FrameEnum.First_Max;

    public ApplicationClass() {
    }

    public ApplicationClass(Class aClass) {
        Package classPackage = aClass.getPackage();
        init(aClass);
    }

    public ApplicationClass(Class aClass, FrameEnum frameEnum) {
        Package classPackage = aClass.getPackage();
        enumToCase=frameEnum;
        init(aClass);
    }


    private void init(Class aClass) {
        //1.初始化spring内部自身的bean
        initIoc();
        //2.需要实例化的bean的路径
        initBean(aClass);
    }

    private void initBean(Class aClass) {
        //获取工厂对象
        BeanFactory beanFactory = (BeanFactory)getBean(enumToCase.myBeanName(BeanFactory.class.getSimpleName()));
        //递归查询传入路径下的所有被@compont注解标记的类
        List<Class<?>> classes = scanClasses(aClass);
        // 处理获取到的Class对象
        classes.forEach(clazz->{
            //当前类不是注解时才会判断
            if (!clazz.isAnnotation()){
                //获取当前类上的注解
                Annotation[] annotations = clazz.getAnnotations();
                //是否是bean
                if(isBean(annotations)){
                   beanFactory.foundBean(new BeanDefinition(clazz, enumToCase.myBeanName(clazz.getSimpleName())));
                }
                //当前类是否为配置类
                if (isConfig(annotations)){
                    configSetBean(beanFactory, clazz);
                }
            }
        });
    }

    /**
     * 通过配置类去创建bean
     * @param beanFactory
     * @param clazz
     */
    private void configSetBean(BeanFactory beanFactory, Class<?> clazz) {
        //获取当前配置类的方法集合
        for (Method method : clazz.getMethods()) {
            if (Arrays.stream(method.getAnnotations()).filter(v-> v instanceof Bean).collect(Collectors.toList()).size()>0){
                try {
                    Object configuration = getBean(enumToCase.myBeanName(clazz.getSimpleName()));
                    Object invoke = method.invoke(configuration);
                    String beanName = method.getName().substring(method.getName().indexOf("get") + 3);
                    beanFactory.instanceBean(invoke, enumToCase.myBeanName(beanName));
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    private boolean isConfig(Annotation[] annotations) {
        boolean tag=false;
        for (Annotation annotation : annotations) {
            if (annotation instanceof Configuration){
                tag=true;
            }
            //如果当前注解其内部被Component修饰了
            if ( Arrays.stream(annotation.annotationType().getAnnotations())
                    .filter(v-> !(v instanceof Documented || v instanceof Target || v instanceof Retention))
                    .collect(Collectors.toList()).size()>0){
                tag=isBean(annotation.annotationType().getAnnotations());
            }
        }
        return tag;
    }

    private boolean isBean(Annotation[] annotations) {
        boolean tag=false;
        for (Annotation annotation : annotations) {
            if (annotation instanceof Component){
               tag=true;
            }
            //如果当前注解其内部被Component修饰了
            if ( Arrays.stream(annotation.annotationType().getAnnotations())
                            .filter(v-> !(v instanceof Documented || v instanceof Target || v instanceof Retention))
                            .collect(Collectors.toList()).size()>0){
                tag=isBean(annotation.annotationType().getAnnotations());
            }
        }
        return tag;
    }

    public static List<Class<?>> scanClasses(Class aClassh) {
        List<Class<?>> classes = new ArrayList<>();
        URL resource = ApplicationClass.class.getResource("");
        // 获取文件夹的绝对路径
        URL folderUrl = aClassh.getResource("");
        try {
            File folder = new File(folderUrl.toURI());
            scanClassesRecursive(folder, classes);
            scanClassesRecursive(new File(Paths.get(resource.toURI()).getParent().toUri()),classes);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return classes;
    }

    private static void scanClassesRecursive(File folder, List<Class<?>> classes) throws IOException, ClassNotFoundException {
        if (!folder.exists() || !folder.isDirectory()) {
            return;
        }
        for (File file : folder.listFiles()) {
            if (file.isDirectory()) {
                scanClassesRecursive(file, classes);
            } else if (file.getName().endsWith(".class")) {
                String className = getClassName(file.getAbsolutePath());
                Class<?> clazz = Class.forName(className.substring(className.indexOf("com")));
                classes.add(clazz);
            }
        }
    }

    private static String getClassName(String filePath) {
        // 根据文件路径获取类的全限定名
        String className = filePath.replace(File.separator, ".");
        className = className.substring(className.indexOf(".") + 1, className.lastIndexOf("."));
        return className;
    }

    /**
     * ioc容器初始化
     */
    private void initIoc() {
        BeanFactory beanFactory=new BeanFactory();
        beanFactory.foundBean(new BeanDefinition(beanFactory.getClass(),beanFactory.getClass().getSimpleName()));
    }

    public Class getaClass() {
        return aClass;
    }

    public void setaClass(Class aClass) {
        this.aClass = aClass;
    }

    public String getBeanPackage() {
        return beanPackage;
    }

    public void setBeanPackage(String beanPackage) {
        this.beanPackage = beanPackage;
    }
}
